#include "JSValueWrapper.h"

using namespace AwesomiumDotNet;

JSValue::JSValue(const Awesomium::JSValue& nativeValue)
{
	if (nativeValue.isBoolean())
	{
		value = nativeValue.toBoolean();
	}
	else if (nativeValue.isDouble())
	{
		value = nativeValue.toDouble();
	}
	else if (nativeValue.isInteger())
	{
		value = nativeValue.toInteger();
	}
	else if (nativeValue.isString())
	{
		value = gcnew String(nativeValue.toString().c_str());
	}
	else if (nativeValue.isNull())
	{
		value = nullptr;
	}
	else if (nativeValue.isArray())
	{
		Awesomium::JSValue::Array nativeArray = nativeValue.getArray();
		ICollection<JSValue^>^ arr = gcnew Collection<JSValue^>();

		for each (Awesomium::JSValue value in nativeArray)
			arr->Add(gcnew JSValue(value));

		value = arr;
	}
	else if (nativeValue.isObject())
	{
		Awesomium::JSValue::Object nativeObj = nativeValue.getObject();
		IDictionary<String^, JSValue^>^ obj = gcnew Dictionary<String^, JSValue^>(nativeObj.size());
		
		for each(std::pair<std::wstring, Awesomium::JSValue> value in nativeObj)
			obj->Add(gcnew String(value.first.c_str()), gcnew JSValue(value.second));

		value = obj;
	}
}

JSValue::JSValue() {
	value = nullptr;
}

JSValue::JSValue(bool value) {
	this->value = value;
}

JSValue::JSValue(int value) {
	this->value = value;
}

JSValue::JSValue(double value) {
	this->value = value;
}

JSValue::JSValue(String^ value) {
	this->value = value;
}

JSValue::JSValue(IEnumerable<JSValue^>^ value) {
	this->value = value;
}

JSValue::JSValue(IDictionary<String^, JSValue^>^ value) {
	this->value = value;
}


bool JSValue::IsBoolean::get() {
	return (dynamic_cast<bool^>(value) != nullptr);
}

bool JSValue::IsInteger::get() {
	return (dynamic_cast<int^>(value) != nullptr);
}

bool JSValue::IsDouble::get() {
	return (dynamic_cast<double^>(value) != nullptr);
}

bool JSValue::IsString::get() {
	return (dynamic_cast<String^>(value) != nullptr);
}

bool JSValue::IsNull::get() {
	return (value == nullptr);
}

bool JSValue::IsArray::get() {
	return (dynamic_cast<IEnumerable<JSValue^>^>(value) != nullptr);
}

bool JSValue::IsObject::get() {
	return (dynamic_cast<IDictionary<String^, JSValue^>^>(value) != nullptr);
}


String^ JSValue::ToString()
{
	if (IsString)
		return value->ToString();
	else if (IsBoolean)
		return value->ToString()->ToLower();
	else if (IsNull || IsArray || IsObject)
		return "";
	else
		return value->ToString();
}

bool JSValue::ToBoolean()
{
	if (IsBoolean)
		return (bool)value;
	else
		return false;
}

int JSValue::ToInteger()
{
	if (IsInteger || IsDouble)
		return (int)value;
	else
		return 0;
}

double JSValue::ToDouble()
{
	if (IsDouble || IsInteger)
		return (double)value;
	else
		return 0.0;
}


IEnumerable<JSValue^>^ JSValue::GetArray()
{
	if (IsArray)
		return (IEnumerable<JSValue^>^)value;
	else
		return nullptr;
}

IDictionary<String^, JSValue^>^ JSValue::GetObject()
{
	if (IsObject)
		return (IDictionary<String^, JSValue^>^)value;
	else
		return nullptr;
}


Awesomium::JSValue JSValue::ToNative()
{
	if (IsNull) {
		return Awesomium::JSValue();
	}
	else if (IsBoolean) {
		return Awesomium::JSValue(ToBoolean());
	}
	else if (IsDouble) {
		return Awesomium::JSValue(ToDouble());
	}
	else if (IsInteger) {
		return Awesomium::JSValue(ToInteger());
	}
	else if (IsString) {
		wchar_t* stringTemp = MarshalStringW(ToString());
		Awesomium::JSValue value = Awesomium::JSValue(stringTemp);
		FreeUnmanagedString(stringTemp);
		return value;
	}
	else if (IsArray)
	{
		IEnumerable<JSValue^>^ arr = (IEnumerable<JSValue^>^)value;
		Awesomium::JSValue::Array nativeArr;

		for each (JSValue^ value in arr)
			nativeArr.push_back(value->ToNative());

		return Awesomium::JSValue(nativeArr);
	}
	else if (IsObject)
	{
		IDictionary<String^, JSValue^>^ obj = (Dictionary<String^, JSValue^>^)value;
		Awesomium::JSValue::Object nativeObj;

		for each (KeyValuePair<String^, JSValue^> value in obj)
			nativeObj.insert(std::pair<std::wstring, Awesomium::JSValue>(MarshalStringW(value.Key), value.Value->ToNative()));

		return Awesomium::JSValue(nativeObj);
	}
	else {
		throw gcnew ArgumentException("Invalid type");
	}
	return Awesomium::JSValue();
}


JSValue^ FutureJSValue::Get()
{
	return gcnew JSValue(futureJSValue->get());
}

JSValue^ FutureJSValue::GetWithTimeout(int timeoutMs)
{
	return gcnew JSValue(futureJSValue->getWithTimeout(timeoutMs));
}
